﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint;
using DataModel;

namespace SPFetch
{
    public class Program
    {

        public static void Main(String[] args)
        {
            //Are you happy now, compiler?
        }

        /// <summary>
        /// Makes the string XML compatible, removing all special characters and replacing them with their XML encoded versions.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <returns></returns>
        private string scrub(string s)
        {
            string questionablePlaceHolder = "!_!_5431534_!_!";
            s = s.Replace("&", questionablePlaceHolder);
            s = s.Replace("<", "&lt;");
            s = s.Replace(">", "&gt;");
            s = s.Replace("'", "&apos;");
            s = s.Replace("\"", "&quot;");
            s = s.Replace(questionablePlaceHolder, "&amp;");

            return s;
        }

        /* 
         * Side note: This is poor program design, the XML specification tied to the SaveFile format is hard coded too strongly into this.
         * This is because originally SPFetch was a standalone program, so it produced string/XML instead of plain content types / site columns that 
         * use the SaveFile data model. I could refactor it at this point but it's probably not worth the effort now.
         * */
        /// <summary>
        /// Runs the progrma against the specified URL.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <returns></returns>
        public string run(string url)
        {
            StringBuilder sb = new StringBuilder();

            using (SPSite site = new SPSite(url))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    Log.Append("Dump starting.").AppendLine();
                    sb.AppendLine("<SPDump>");
                    sb.AppendLine("\t<SiteColumnsDump>");
                    foreach (SPField field in web.AvailableFields)
                    {
                        // WILL grab hidden fields (in case non-hiden content types reference hidden fields)
                        if (true) //(!field.Hidden && field.Group != "_Hidden")
                        {
                            try
                            {
                                //sb.AppendLine(field.SchemaXml);
                                sb.Append("\t\t<SiteColumnDump ");
                                sb.Append("GUID=\"" + scrub(field.Id.ToString("B")) + "\" ");
                                sb.Append("Name=\"").Append(scrub(field.Title)).Append("\" ");
                                sb.Append("InternalName=\"").Append(scrub(field.InternalName)).Append("\" ");
                                sb.Append("Type=\"").Append(scrub(field.TypeAsString)).Append("\" ");
                                sb.Append("TypeDisplayName=\"").Append(scrub(field.TypeDisplayName)).Append("\" ");
                                sb.Append("Required=\"").Append(field.Required ? "TRUE" : "FALSE").Append("\" ");
                                sb.Append("Unique=\"").Append(field.EnforceUniqueValues ? "TRUE" : "FALSE").Append("\" ");
                                sb.AppendLine("/>");
                            }
                            catch (Exception ex)
                            {
                                Log.Append(ex.Message).AppendLine();
                            }
                        }
                    }
                    sb.AppendLine("\t</SiteColumnsDump>");
                    sb.AppendLine("\t<ContentTypesDump>");
                    foreach (SPContentType ct in web.AvailableContentTypes)
                    {
                        // will not find hidden content types to dump.
                        if (!ct.Hidden && ct.Group != "_Hidden")
                        {
                            try
                            {
                                //sb.AppendLine(ct.SchemaXml);
                                sb.Append("\t\t<ContentTypeDump ");
                                sb.Append("ID=\"").Append(scrub(ct.Id.ToString())).Append("\" ");
                                sb.Append("Name=\"").Append(scrub(ct.Name)).Append("\" ");
                                sb.AppendLine(">");

                                foreach (SPField field in ct.Fields)
                                {
                                    sb.Append("\t\t\t<Ref InternalName=\"").Append(scrub(field.InternalName)).AppendLine("\" />");
                                }

                                int linkCount = ct.FieldLinks.Count, fieldCount = ct.Fields.Count;
                                Log.Append("[F:" + fieldCount + "|L:" + linkCount + "] " + ct.Name).AppendLine();
                                if (fieldCount != linkCount) { Log.Append("  ^--- Field Vs FieldLink mismatch.").AppendLine(); }
                                sb.AppendLine("\t\t</ContentTypeDump>");
                            }
                            catch (Exception ex) 
                            {
                                Log.Append(ex.Message).AppendLine();
                            }
                        }
                    }
                    sb.AppendLine("\t</ContentTypesDump>");
                    sb.AppendLine("</SPDump>");
                    Log.Append("Dump complete.").AppendLine();
                    return sb.ToString();
                }
            }
        }
    }
}
